// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
// import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr;
// import com.alibaba.druid.sql.ast.statement.SQLExternalRecordFormat;
// import com.alibaba.druid.sql.ast.statement.SQLSelect;
// import com.alibaba.druid.sql.ast.statement.SQLTableElement;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleLobStorageClause;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleStorageClause;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->*;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->OracleCreateTableStatement.DeferredSegmentCreation;
// import com.alibaba.druid.sql.parser.Lexer;
// import com.alibaba.druid.sql.parser.ParserException;
// import com.alibaba.druid.sql.parser.SQLCreateTableParser;
// import com.alibaba.druid.sql.parser.Token;
// import com.alibaba.druid.util.FnvHash;

#include "OracleCreateTableParser.h"
#include "OracleExprParser.h"
#include "../../../parser/SQLDDLParser.h"
#include "../ast/stmt/OracleCreateTableStatement.h"
#include "../ast/stmt/OracleXmlColumnProperties.h"
#include "../ast/stmt/OracleLobParameters.h"
#include "../ast/stmt/OracleSupplementalLogGrp.h"
#include "../ast/stmt/OracleSupplementalIdKey.h"
#include "../../../../utils/FnvHash.h"
#include "../../../ast/SQLPartitionBy.h"
#include "../../../ast/SQLPartitionByRange.h"
#include "../../../ast/SQLPartitionByHash.h"
#include "../../../ast/SQLPartitionByList.h"
#include "../ast/clause/OracleStorageClause.h"
#include "OracleSelectParser.h"
#include "../../../ast/expr/SQLNumericLiteralExpr.h"
#include "../../../ast/statement/SQLExternalRecordFormat.h"

OracleCreateTableParser::OracleCreateTableParser(Lexer_ptr lexer)
    : SQLCreateTableParser(OracleExprParser_ptr(new OracleExprParser(lexer)))
{
}

OracleCreateTableParser::OracleCreateTableParser(string_ptr sql)
    : SQLCreateTableParser(OracleExprParser_ptr(new OracleExprParser(sql)))
{
}

OracleCreateTableStatement_ptr OracleCreateTableParser::newCreateStatement()
{
  return OracleCreateTableStatement_ptr(new OracleCreateTableStatement());
}

OracleCreateTableStatement_ptr OracleCreateTableParser::parseCreateTable(BOOL_ptr acceptCreate)
{
  OracleCreateTableStatement_ptr stmt = std::dynamic_pointer_cast<OracleCreateTableStatement>(SQLCreateTableParser::parseCreateTable(acceptCreate));

  if (lexer->token()->name->c_str() == Token::OF.name->c_str())
  {
    lexer->nextToken();
    stmt->setOf(this->exprParser->name());

    if (lexer->identifierEquals(make_string_ptr("OIDINDEX")))
    {
      lexer->nextToken();

      OracleCreateTableStatement_OIDIndex_ptr oidIndex = OracleCreateTableStatement_OIDIndex_ptr(new OracleCreateTableStatement_OIDIndex());

      if (lexer->token()->name->c_str() != Token::LPAREN.name->c_str())
      {
        oidIndex->setName(this->exprParser->name());
      }
      accept(Token::LPAREN);
      this->getExprParser()->parseSegmentAttributes(oidIndex);
      accept(Token::RPAREN);

      stmt->setOidIndex(oidIndex);
    }
  }

  for (;;)
  {
    this->getExprParser()->parseSegmentAttributes(stmt);

    if (lexer->identifierEquals(FnvHash::Constants::IN_MEMORY_METADATA))
    {
      lexer->nextToken();
      stmt->setInMemoryMetadata(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::CURSOR_SPECIFIC_SEGMENT))
    {
      lexer->nextToken();
      stmt->setCursorSpecificSegment(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::NOPARALLEL))
    {
      lexer->nextToken();
      stmt->setParallel(BOOL::FALSE);
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::PARALLEL))
    {
      lexer->nextToken();
      stmt->setParallel(BOOL::TRUE);

      if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
      {
        stmt->setParallelValue(this->exprParser->primary());
      }
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::CACHE.name->c_str())
    {
      lexer->nextToken();
      stmt->setCache(BOOL::TRUE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::NOCACHE.name->c_str())
    {
      lexer->nextToken();
      stmt->setCache(BOOL::FALSE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() == Token::ROW.name->c_str())
      {
        lexer->nextToken();
        acceptIdentifier("MOVEMENT");
        stmt->setEnableRowMovement(BOOL::TRUE);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
      // stmt->setEnable(BOOL::TRUE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() == Token::ROW.name->c_str())
      {
        lexer->nextToken();
        acceptIdentifier("MOVEMENT");
        stmt->setEnableRowMovement(BOOL::FALSE);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
      // stmt->setEnable(BOOL::FALSE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::ON.name->c_str())
    {
      lexer->nextToken();
      accept(Token::COMMIT);

      if (lexer->identifierEquals(make_string_ptr("PRESERVE")))
      {
        lexer->nextToken();
        acceptIdentifier("ROWS");
        stmt->setOnCommitPreserveRows(BOOL::TRUE);
      }
      else
      {
        accept(Token::DELETE);
        acceptIdentifier("ROWS");
        stmt->setOnCommitDeleteRows(BOOL::TRUE);
      }
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("STORAGE")))
    {
      OracleStorageClause_ptr storage = (std::dynamic_pointer_cast<OracleExprParser>(this->exprParser))->parseStorage();
      stmt->setStorage(storage);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("ORGANIZATION")))
    {
      parseOrganization(stmt);
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::CLUSTER))
    {
      lexer->nextToken();
      SQLName_ptr cluster = this->exprParser->name();
      stmt->setCluster(cluster);
      accept(Token::LPAREN);
      this->exprParser->names(stmt->getClusterColumns(), cluster);
      accept(Token::RPAREN);
      continue;
      //            } else if (lexer->token()->name->c_str() == Token::STORAGE) {
      //                OracleStorageClause storage = ((OracleExprParser) this->exprParser).parseStorage();
      //                stmt->setStorage(storage);
      //                continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("MONITORING")))
    {
      lexer->nextToken();
      stmt->setMonitoring(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::INCLUDING))
    {
      lexer->nextToken();
      this->exprParser->names(stmt->getIncluding(), stmt);
      acceptIdentifier("OVERFLOW");
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::LOB.name->c_str())
    {
      OracleLobStorageClause_ptr lobStorage = (std::dynamic_pointer_cast<OracleExprParser>(this->exprParser))->parseLobStorage();
      stmt->setLobStorage(lobStorage);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::SEGMENT.name->c_str())
    {
      lexer->nextToken();
      accept(Token::CREATION);
      if (lexer->token()->name->c_str() == Token::IMMEDIATE.name->c_str())
      {
        lexer->nextToken();
        stmt->setDeferredSegmentCreation(OracleCreateTableStatement_DeferredSegmentCreation::IMMEDIATE);
      }
      else
      {
        accept(Token::DEFERRED);
        stmt->setDeferredSegmentCreation(OracleCreateTableStatement_DeferredSegmentCreation::DEFERRED);
      }
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::COLUMN.name->c_str())
    {
      lexer->nextToken();
      SQLName_ptr name = this->exprParser->name();
      if (lexer->token()->name->c_str() == Token::NOT.name->c_str())
      {
        lexer->nextToken();
      }

      if (lexer->identifierEquals(FnvHash::Constants::SUBSTITUTABLE))
      {
        lexer->nextToken();
        acceptIdentifier("AT");
        accept(Token::ALL);
        acceptIdentifier("LEVELS");
      }
      // skip
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::VARRAY))
    {
      lexer->nextToken();
      SQLName_ptr name = this->exprParser->name();

      accept(Token::STORE);
      accept(Token::AS);
      if (lexer->identifierEquals(FnvHash::Constants::BASICFILE))
      {
        lexer->nextToken();
      }
      this->getExprParser()->parseLobStorage();
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    else if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
    {
      lexer->nextToken();

      accept(Token::BY);

      if (lexer->identifierEquals(make_string_ptr("RANGE")))
      {
        SQLPartitionByRange_ptr partitionByRange = this->getExprParser()->partitionByRange();
        this->getExprParser()->partitionClauseRest(partitionByRange);
        stmt->setPartitioning(partitionByRange);
        continue;
      }
      else if (lexer->identifierEquals(make_string_ptr("HASH")))
      {
        SQLPartitionByHash_ptr partitionByHash = this->getExprParser()->partitionByHash();
        this->getExprParser()->partitionClauseRest(partitionByHash);

        if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
        {
          lexer->nextToken();
          for (;;)
          {
            SQLPartition_ptr partition = this->getExprParser()->parsePartition();
            partitionByHash->addPartition(partition);
            if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
            {
              lexer->nextToken();
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
            {
              lexer->nextToken();
              break;
            }
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
        }
        stmt->setPartitioning(partitionByHash);
        continue;
      }
      else if (lexer->identifierEquals(make_string_ptr("LIST")))
      {
        SQLPartitionByList_ptr partitionByList_ = partitionByList();
        this->getExprParser()->partitionClauseRest(partitionByList_);
        stmt->setPartitioning(partitionByList_);
        continue;
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }
    else if (lexer->identifierEquals(FnvHash::Constants::XMLTYPE))
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() == Token::COLUMN.name->c_str())
      {
        lexer->nextToken();
      }

      OracleXmlColumnProperties_ptr xmlColumnProperties = OracleXmlColumnProperties_ptr(new OracleXmlColumnProperties());
      xmlColumnProperties->setColumn(this->exprParser->name());

      if (lexer->token()->name->c_str() == Token::STORE.name->c_str())
      {
        lexer->nextToken();
        accept(Token::AS);

        OracleXmlColumnProperties_OracleXMLTypeStorage_ptr storage = OracleXmlColumnProperties_OracleXMLTypeStorage_ptr(new OracleXmlColumnProperties_OracleXMLTypeStorage());
        if (lexer->identifierEquals(make_string_ptr("SECUREFILE")))
        {
          storage->setSecureFile(BOOL::TRUE);
          lexer->nextToken();
        }
        else if (lexer->identifierEquals(make_string_ptr("BASICFILE")))
        {
          storage->setBasicFile(BOOL::TRUE);
          lexer->nextToken();
        }

        if (lexer->identifierEquals(make_string_ptr("BINARY")))
        {
          lexer->nextToken();
          acceptIdentifier("XML");
          storage->setBinaryXml(BOOL::TRUE);
        }
        else if (lexer->identifierEquals(make_string_ptr("CLOB")))
        {
          lexer->nextToken();
          storage->setClob(BOOL::TRUE);
        }

        if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
        {
          lexer->nextToken();

          OracleLobParameters_ptr lobParameters = OracleLobParameters_ptr(new OracleLobParameters());

          // for_:
          for (;;)
          {
            if (lexer->token()->name->c_str() == Token::TABLESPACE.name->c_str())
            {
              {
                lexer->nextToken();
                SQLName_ptr tableSpace = this->exprParser->name();
                lobParameters->setTableSpace(tableSpace);
              }
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str() ||
                     lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
            {
              {
                BOOL_ptr enable = (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())?BOOL::TRUE:BOOL::FALSE;
                lexer->nextToken();
                accept(Token::STORAGE);
                accept(Token::IN);
                accept(Token::ROW);

                lobParameters->setEnableStorageInRow(enable);
              }
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::CHUNK.name->c_str())
            {
              lexer->nextToken();
              SQLExpr_ptr chunk = this->exprParser->expr();
              lobParameters->setChunk(chunk);
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::NOCACHE.name->c_str())
            {
              lexer->nextToken();
              lobParameters->setCache(BOOL::FALSE);
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::LOGGING.name->c_str())
            {
              lexer->nextToken();
              lobParameters->setLogging(BOOL::TRUE);
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::NOCOMPRESS.name->c_str())
            {
              lexer->nextToken();
              lobParameters->setCompress(BOOL::FALSE);
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::KEEP_DUPLICATES.name->c_str())
            {
              lexer->nextToken();
              lobParameters->setKeepDuplicates(BOOL::TRUE);
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::STORAGE.name->c_str())
            {
              OracleStorageClause_ptr storageClause = this->getExprParser()->parseStorage();
              lobParameters->setStorage(storageClause);
              // goto for_;
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
            {
              long hash = lexer->hashLCase();
              if (hash == FnvHash::Constants::PCTVERSION)
              {
                lobParameters->setPctVersion(this->exprParser->primary());
                lexer->nextToken();
                // goto for_;
                continue;
              }
              // break for_;
              break;
            }
            else
            {
              // break for_;
              break;
            }
          }

          accept(Token::RPAREN);

          storage->setLobParameters(lobParameters);
        }
      }

      for (;;)
      {
        if (lexer->identifierEquals(FnvHash::Constants::ALLOW))
        {
          lexer->nextToken();
          if (lexer->identifierEquals(make_string_ptr("NONSCHEMA")))
          {
            lexer->nextToken();
            xmlColumnProperties->setAllowNonSchema(BOOL::TRUE);
          }
          else if (lexer->identifierEquals(make_string_ptr("ANYSCHEMA")))
          {
            lexer->nextToken();
            xmlColumnProperties->setAllowAnySchema(BOOL::TRUE);
          }
          else
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          continue;
        }
        else if (lexer->identifierEquals(FnvHash::Constants::DISALLOW))
        {
          lexer->nextToken();
          if (lexer->identifierEquals(make_string_ptr("NONSCHEMA")))
          {
            lexer->nextToken();
            xmlColumnProperties->setAllowNonSchema(BOOL::FALSE);
          }
          else if (lexer->identifierEquals(make_string_ptr("ANYSCHEMA")))
          {
            lexer->nextToken();
            xmlColumnProperties->setAllowAnySchema(BOOL::FALSE);
          }
          else
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          continue;
        }
        break;
      }

      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));

      stmt->setXmlTypeColumnProperties(xmlColumnProperties);
      continue;
    }

    break;
  }

  if (lexer->token()->name->c_str() == Token::AS.name->c_str())
  {
    lexer->nextToken();

    SQLSelect_ptr select = (new OracleSelectParser(exprParser))->select();
    stmt->setSelect(select);
  }

  return stmt;
}

void OracleCreateTableParser::parseOrganization(OracleCreateTableStatement_ptr stmt)
{
  OracleCreateTableStatement_Organization_ptr organization = OracleCreateTableStatement_Organization_ptr(new OracleCreateTableStatement_Organization());
  acceptIdentifier("ORGANIZATION");
  if (lexer->token()->name->c_str() == Token::INDEX.name->c_str())
  {
    lexer->nextToken();
    organization->setType(make_string_ptr("INDEX"));
    this->getExprParser()->parseSegmentAttributes(organization);

    // index_org_table_clause http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7002.htm#i2129638
    if (lexer->identifierEquals(FnvHash::Constants::PCTTHRESHOLD))
    {
      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
      {
        int pctthreshold = (std::dynamic_pointer_cast<SQLNumericLiteralExpr>(this->exprParser->primary()))->getNumber();
        organization->setPctthreshold(pctthreshold);
      }
    }
  }
  else if (lexer->identifierEquals(make_string_ptr("HEAP")))
  {
    lexer->nextToken();
    organization->setType(make_string_ptr("HEAP"));
    this->getExprParser()->parseSegmentAttributes(organization);
  }
  else if (lexer->identifierEquals(make_string_ptr("EXTERNAL")))
  {
    lexer->nextToken();
    organization->setType(make_string_ptr("EXTERNAL"));
    accept(Token::LPAREN);

    if (lexer->identifierEquals(make_string_ptr("TYPE")))
    {
      lexer->nextToken();
      organization->setExternalType(this->exprParser->name());
    }

    accept(Token::DEFAULT);
    acceptIdentifier("DIRECTORY");

    organization->setExternalDirectory(this->exprParser->expr());

    if (lexer->identifierEquals(make_string_ptr("ACCESS")))
    {
      lexer->nextToken();
      acceptIdentifier("PARAMETERS");

      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();

        SQLExternalRecordFormat_ptr recordFormat = SQLExternalRecordFormat_ptr(new SQLExternalRecordFormat());

        if (lexer->identifierEquals(make_string_ptr("RECORDS")))
        {
          lexer->nextToken();

          if (lexer->identifierEquals(make_string_ptr("DELIMITED")))
          {
            lexer->nextToken();
            accept(Token::BY);

            if (lexer->identifierEquals(make_string_ptr("NEWLINE")))
            {
              lexer->nextToken();
              recordFormat->setDelimitedBy(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("NEWLINE"))));
            }
            else
            {
              std::string tmp = "TODO ";
              tmp += encode_util::UnicodeToUTF8(lexer->info());
              throw new ParserException(make_string_ptr(tmp));
            }

            if (lexer->identifierEquals(FnvHash::Constants::NOLOGFILE))
            {
              lexer->nextToken();
              recordFormat->setLogfile(BOOL::FALSE);
            }

            if (lexer->identifierEquals(FnvHash::Constants::NOBADFILE))
            {
              lexer->nextToken();
              recordFormat->setBadfile(BOOL::FALSE);
            }
          }
          else
          {
            std::string tmp = "TODO ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
        }

        if (lexer->identifierEquals(FnvHash::Constants::FIELDS))
        {
          lexer->nextToken();

          if (lexer->identifierEquals(FnvHash::Constants::TERMINATED))
          {
            lexer->nextToken();
            accept(Token::BY);
            recordFormat->setTerminatedBy(this->exprParser->primary());
          }
          else
          {
            std::string tmp = "TODO ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }

          if (lexer->identifierEquals(FnvHash::Constants::LTRIM))
          {
            lexer->nextToken();
            recordFormat->setLtrim(BOOL::TRUE);
          }
        }

        if (lexer->identifierEquals(FnvHash::Constants::MISSING))
        {
          lexer->nextToken();
          acceptIdentifier("FIELD");
          accept(Token::VALUES);
          acceptIdentifier("ARE");
          accept(Token::XNULL);
          recordFormat->setMissingFieldValuesAreNull(BOOL::TRUE);
        }

        if (lexer->token()->name->c_str() == Token::REJECT.name->c_str())
        {
          lexer->nextToken();
          acceptIdentifier("ROWS");
          accept(Token::WITH);
          accept(Token::ALL);
          accept(Token::XNULL);
          acceptIdentifier("FIELDS");
          recordFormat->setRejectRowsWithAllNullFields(BOOL::TRUE);
        }

        organization->setExternalDirectoryRecordFormat(recordFormat);
        accept(Token::RPAREN);
      }
      else if (lexer->token()->name->c_str() == Token::USING.name->c_str())
      {
        lexer->nextToken();
        acceptIdentifier("CLOB");
        std::string tmp = "TODO ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }

    acceptIdentifier("LOCATION");
    accept(Token::LPAREN);
    this->exprParser->exprList(organization->getExternalDirectoryLocation(), organization);
    accept(Token::RPAREN);

    accept(Token::RPAREN);

    if (lexer->token()->name->c_str() == Token::REJECT.name->c_str())
    {
      lexer->nextToken();
      accept(Token::LIMIT);

      organization->setExternalRejectLimit(this->exprParser->primary());
    }
    //
  }
  else
  {
    std::string tmp = "TODO ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }
  stmt->setOrganization(organization);
}

SQLPartitionByList_ptr OracleCreateTableParser::partitionByList()
{
  acceptIdentifier("LIST");
  SQLPartitionByList_ptr partitionByList = SQLPartitionByList_ptr(new SQLPartitionByList());

  accept(Token::LPAREN);
  partitionByList->addColumn(this->exprParser->expr());
  accept(Token::RPAREN);

  this->getExprParser()->parsePartitionByRest(partitionByList);

  return partitionByList;
}

SQLTableElement_ptr OracleCreateTableParser::parseCreateTableSupplementalLogingProps()
{
  acceptIdentifier("SUPPLEMENTAL");
  acceptIdentifier("LOG");

  if (lexer->token()->name->c_str() == Token::GROUP.name->c_str())
  {
    lexer->nextToken();

    OracleSupplementalLogGrp_ptr logGrp = OracleSupplementalLogGrp_ptr(new OracleSupplementalLogGrp());
    logGrp->setGroup(this->exprParser->name());

    accept(Token::LPAREN);
    for (;;)
    {
      SQLName_ptr column = this->exprParser->name();

      if (lexer->identifierEquals(make_string_ptr("NO")))
      {
        lexer->nextToken();
        acceptIdentifier("LOG");
        Object_ptr tmpObject = Object_ptr(new Object(true));
        column->putAttribute("NO LOG", tmpObject);
      }

      logGrp->addColumn(column);

      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }

      if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
      {
        break;
      }

      std::string tmp = "TODO ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    accept(Token::RPAREN);

    if (lexer->identifierEquals(make_string_ptr("ALWAYS")))
    {
      lexer->nextToken();
      logGrp->setAlways(BOOL::TRUE);
    }

    return logGrp;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::DATA))
  {
    lexer->nextToken();

    OracleSupplementalIdKey_ptr idKey = OracleSupplementalIdKey_ptr(new OracleSupplementalIdKey());
    accept(Token::LPAREN);
    for (;;)
    {
      if (lexer->token()->name->c_str() == Token::ALL.name->c_str())
      {
        lexer->nextToken();
        idKey->setAll(BOOL::TRUE);
      }
      else if (lexer->token()->name->c_str() == Token::PRIMARY.name->c_str())
      {
        lexer->nextToken();
        accept(Token::KEY);
        idKey->setPrimaryKey(BOOL::TRUE);
      }
      else if (lexer->token()->name->c_str() == Token::UNIQUE.name->c_str())
      {
        lexer->nextToken();

        if (lexer->token()->name->c_str() == Token::INDEX.name->c_str())
        {
          lexer->nextToken();
          idKey->setUniqueIndex(BOOL::TRUE);
        }
        else
        {
          idKey->setUnique(BOOL::TRUE);
        }
      }
      else if (lexer->token()->name->c_str() == Token::FOREIGN.name->c_str())
      {
        lexer->nextToken();
        accept(Token::KEY);
        idKey->setForeignKey(BOOL::TRUE);
      }

      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }

      if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
      {
        break;
      }

      std::string tmp = "TODO ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    accept(Token::RPAREN);
    acceptIdentifier("COLUMNS");

    return idKey;
  }

  std::string tmp = "TODO ";
  tmp += encode_util::UnicodeToUTF8(lexer->info());
  throw new ParserException(make_string_ptr(tmp));
}

// @Override
OracleExprParser_ptr OracleCreateTableParser::getExprParser()
{
  return std::dynamic_pointer_cast<OracleExprParser>(exprParser);
}
